/*
 * @Copyright: Citylife 2021
 * @Copyright: EasyCompany 2021
 * @Author: asterisk
 * @Date: 2021-03-18 11:48:10
 * @Description: 
 */
import 'dart:convert';

import 'package:sqflite/sqflite.dart';
import 'package:sqflite/sqlite_api.dart';

import '../../easy_log.dart';
import 'dao.dart';
import 'log_internal.dart';

LogDao createDao() {
  return LogDaoApp.instance;
}

class LogDaoApp implements LogDao {
  static const String TAG = 'LogDaoApp';
  static LogDaoApp instance = LogDaoApp();

  String _root;
  String _appKey;
  String _uid;
  Database _db;
  final int _version;

  LogDaoApp([this._version = 1]);

  void dispose() {}
  @override
  Future init() {
    return null;
  }

  Future upgradeTable(Transaction txn, int currentVersion) async {
    if (0 == currentVersion) {
      var stmt = '''
CREATE TABLE
IF
	NOT EXISTS log (
	auto_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
  level INTEGER NOT NULL,
  timestamp INTEGER NOT NULL,
  tags VARCHAR ( 16 ) NOT NULL,
  content VARCHAR ( 32 ) NOT NULL,
  log_name VARCHAR ( 16 ) NOT NULL,
  class_name VARCHAR ( 16 ),
  func_name VARCHAR ( 16 ) )
''';
      await txn.execute(stmt);
      stmt = '''
CREATE INDEX
IF
	NOT EXISTS timestamp_index ON log (
	timestamp)
''';
      await txn.execute(stmt);
      stmt = '''
CREATE INDEX
IF
	NOT EXISTS log_name_index ON log (
	log_name)
''';
      await txn.execute(stmt);
      stmt = '''
CREATE INDEX
IF
	NOT EXISTS class_name_index ON log (
	class_name)
''';
      await txn.execute(stmt);
      stmt = '''
CREATE INDEX
IF
	NOT EXISTS func_name_index ON log (
	func_name)
''';
      await txn.execute(stmt);
    } else if (1 == currentVersion) {
    } else if (2 == currentVersion) {
    } else {}
  }

  Batch getBatch() {
    return _db.batch();
  }

  Future commit(Batch batch, {bool noResult = true}) async {
    return await batch.commit(noResult: noResult, continueOnError: false);
  }

  EasyLogMessage _queryResult(Map<String, dynamic> item) {
    var level;
    var timestamp;
    var tags;
    var content;
    var autoId;
    item.forEach((key, value) {
      if (key == 'auto_id') {
        autoId = value;
      } else if (key == 'level') {
        level = EasyLogLevel.parse(value);
      } else if (key == 'timestamp') {
        timestamp = value;
      } else if (key == 'tags') {
        tags = json.decoder.convert(value);
      } else if (key == 'content') {
        content = json.decoder.convert(value);
      } else {
        EasyLogInternal.infoPrint('$TAG queryResult: not support: $autoId');
      }
    });
    return EasyLogMessage(
        tags: tags, timestamp: timestamp, level: level, message: content);
  }

  void addOneInternal(EasyLogMessage message, {Batch batch}) {
    var appName = message.tags['appName'];
    var logName = message.tags['logName'];
    var className = message.tags['className'];
    var functionName = message.tags['functionName'];
    batch ??= getBatch();
    var stmt = '''
INSERT INTO log (
level,
timestamp,
tags,
content,
log_name,
class_name,
func_name
)
VALUES
	(?,?,?,?,?,?,?)  
''';
    var values = [
      message.level.value,
      message.timestamp,
      message.tags.toString(),
      json.encoder.convert(message.message),
      null != appName ? appName : logName,
      className,
      functionName
    ];
    batch.rawInsert(stmt, values);
  }

  @override
  Future unInit() {
    return null;
  }

  @override
  Future open(String root, String appKey, String uid) async {
    EasyLogInternal.infoPrint('$TAG open: $root: $appKey: $uid');
    dispose();
    _root = root;
    _appKey = appKey;
    _uid = uid;
    try {
      _db = await openDatabase('${_root}/${_appKey}_${_uid}_log.db',
          version: _version,
          onUpgrade: (Database database, int oldVersion, int newVersion) async {
        await database.transaction((txn) async {
          var currentVersion = oldVersion;
          while (currentVersion <= newVersion) {
            await upgradeTable(txn, currentVersion);
            ++currentVersion;
          }
        });
      });
    } catch (e) {
      EasyLogInternal.errorPrint('$TAG open: error: $e');
    }
    var version = await _db.getVersion();
    var path = _db.path;
    EasyLogInternal.infoPrint('$TAG open: success: ${version}: $path');
  }

  @override
  Future close() async {
    EasyLogInternal.infoPrint('$TAG close: ');
    await _db.close();
  }

  @override
  Future clearOldMessage({int timestamp}) async {
    if (null == timestamp) {
      /// 7天前的日志清理
      var n = DateTime.now();
      n = n.subtract(Duration(days: 7));
      timestamp = n.millisecondsSinceEpoch;
    }
    EasyLogInternal.infoPrint('$TAG clearOldMessage: $timestamp');
    var batch = getBatch();
    var stmt = '''
DELETE FROM log WHERE timestamp < ?
''';
    batch.rawDelete(stmt, [timestamp]);
    await batch.commit();
  }

  @override
  Future addOne(EasyLogMessage message) async {
    EasyLogInternal.infoPrint('$TAG addOne: $message');
    var batch = getBatch();
    addOneInternal(message, batch: batch);
    await batch.commit();
  }

  @override
  Future addMore(List<EasyLogMessage> messages) async {
    EasyLogInternal.infoPrint('$TAG addMore: ${messages.length}');
    var batch = getBatch();
    for (var message in messages) {
      addOneInternal(message, batch: batch);
    }
    await batch.commit();
  }

  @override
  Future<List<EasyLogMessage>> getLastOneDay() async {
    EasyLogInternal.infoPrint('$TAG getLastOneDay');
    var ret = <EasyLogMessage>[];
    var n = DateTime.now();
    n = n.subtract(Duration(days: 1));
    var timestamp = n.millisecondsSinceEpoch;
    var stmt = '';
    stmt = '''
SELECT * FROM message 
 WHERE timestamp >= ?
''';
    var result = await _db.rawQuery(stmt, [timestamp]);
    for (var item in result) {
      ret.add(_queryResult(item));
    }
    return ret;
  }

  @override
  Future<List<EasyLogMessage>> getOneDay() async {
    EasyLogInternal.infoPrint('$TAG getOneDay');
    var ret = <EasyLogMessage>[];
    var n = DateTime.now();
    var currentTimestamp = n.millisecondsSinceEpoch;
    n = n.subtract(Duration(days: 1));
    var beforeTimestamp = n.millisecondsSinceEpoch;
    var stmt = '';
    stmt = '''
SELECT * FROM message 
 WHERE timestamp >= ? AND timestamp < ?
''';
    var result = await _db.rawQuery(stmt, [beforeTimestamp, currentTimestamp]);
    for (var item in result) {
      ret.add(_queryResult(item));
    }
    return ret;
  }
}
